package com.ruoyi.tranShort.service.impl;

import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.tranShort.domain.*;
import com.ruoyi.tranShort.domain.request.WristletRequest;
import com.ruoyi.tranShort.mapper.ShortPersonMapper;
import com.ruoyi.tranShort.mapper.ShortSchedulingDetailMapper;
import com.ruoyi.tranShort.mapper.ShortSentryPostMapper;
import com.ruoyi.tranShort.mapper.ShortWristbandDerviceMapper;
import com.ruoyi.tranShort.mqtt.api.GatewayResponse;
import com.ruoyi.tranShort.mqtt.client.GatewayDefaultClient;
import com.ruoyi.tranShort.mqtt.request.*;
import com.ruoyi.tranShort.mqtt.request.entity.CloseDeviceData;
import com.ruoyi.tranShort.mqtt.request.entity.ConnectDeviceLongData;
import com.ruoyi.tranShort.mqtt.response.*;
import com.ruoyi.tranShort.serialPort.client.SerialPortDefaultClient;
import com.ruoyi.tranShort.serialPort.request.WristletPortRequest;
import com.ruoyi.tranShort.service.IWristletService;
import com.ruoyi.tranShort.utils.MqttClientUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.*;

/**
 * @author wwy
 * @date 2021-08-13 14:44
 */
@Service("wristletService")
public class WristletServiceImpl implements IWristletService {
    private static final Logger LOGGER = LoggerFactory.getLogger(WristletServiceImpl.class);
    @Autowired
    private ShortPersonMapper shortPersonMapper;
    @Autowired
    private ShortSchedulingDetailMapper shortSchedulingDetailMapper;
    @Autowired
    private ShortSentryPostMapper shortSentryPostMapper;
    @Autowired
    private ShortWristbandDerviceMapper shortWristbandDerviceMapper;

    /**
     * mqtt主题模板
     */
    @Value("${mqtt.topicTemplate}")
    private String topicTemplate;
    /**
     * 发送消息设备mac地址
     */
    public static LinkedList<String> sendMacList = new LinkedList<>();

    /**
     * 自动叫哨发送消息设备mac地址
     */
    public static LinkedList<String> autoSendMacList = new LinkedList<>();



    /**
     * corePoolSize    线程池核心池的大小
     *  maximumPoolSize 线程池中允许的最大线程数量
     *  keepAliveTime   当线程数大于核心时，此为终止前多余的空闲线程等待新任务的最长时间
     *  unit            keepAliveTime 的时间单位
     *  workQueue       用来储存等待执行任务的队列
     *  threadFactory   创建线程的工厂类
     *   handler         拒绝策略类,当线程池数量达到上线并且workQueue队列长度达到上限时就需要对到来的任务做拒绝处理
     */
    private static ExecutorService executorService = new ThreadPoolExecutor(
                         4,
                        10,
                         0L,
                       TimeUnit.MILLISECONDS,
                        new LinkedBlockingQueue<>(1024),
                        new ThreadPoolExecutor.AbortPolicy()
               );
    /**
     * 手动叫哨
     * @param wristletRequest
     * @return
     */
    @Override
    public AjaxResult manualSend(WristletRequest wristletRequest) {
        if(wristletRequest == null || wristletRequest.getPersonIdList() == null){
            throw new CustomException("参数错误", HttpStatus.BAD_REQUEST);
        }
        List<Long> personIdList = wristletRequest.getPersonIdList();
        if(personIdList.size()<=0){
            throw new CustomException("人员未选择",HttpStatus.BAD_REQUEST);
        }
        List<ShortPerson> shortPeopleList = shortPersonMapper.selectPersonAndWrist(personIdList);
        //保存进入发送设备集合中
        for (ShortPerson shortPerson : shortPeopleList){
            ShortWristbandDervice shortWristbandDervice = shortPerson.getShortWristbandDervice();
            String deviceCode = shortWristbandDervice.getDerviceCode();
            sendMacList.add(deviceCode.toUpperCase());
        }
        //需要发送的消息
        String message = wristletRequest.getMessage();
        executorService.execute(()->{
            LOGGER.info("========================发送消息线程开始执行========================");
            LOGGER.info("发送消息内容：{}",message);
            for (ShortPerson shortPerson : shortPeopleList) {
                //获取腕带mac
                ShortWristbandDervice shortWristbandDervice = shortPerson.getShortWristbandDervice();
                String deviceCode = shortWristbandDervice.getDerviceCode();
                //获取腕带类型
                Long type = shortWristbandDervice.getType();
                if(type.intValue() == 1){
                    //心率手环，mqtt
                    this.sendMessage(message,deviceCode,0,true);
                }else if(type.intValue() == 2){
                    //串口
                    SerialPortDefaultClient serialPortDefaultClient = new SerialPortDefaultClient();
                    WristletPortRequest wristletPortRequest = new WristletPortRequest();
                    wristletPortRequest.setDeviceCode(deviceCode);
                    serialPortDefaultClient.execute(wristletPortRequest);
                }

            }
            LOGGER.info("========================发送消息线程结束执行========================");
        });
        return AjaxResult.success();
    }

    /**
     * 腕带发送消息
     * @param message 发送内容
     * @param deviceCode 腕带mac地址
     * @param qos qos
     * @param ignoreError 是否忽略错误
     * @return
     */
    public boolean sendMessage(String message,String deviceCode,int qos,boolean ignoreError){
        //获取网关
        String topicMac = MqttClientUtil.getTopicMac(deviceCode);

        String topic = topicTemplate.replace("#mac#", topicMac);
        GatewayDefaultClient gatewayDefaultClient = new GatewayDefaultClient();
        boolean connectSuccess = this.connect(gatewayDefaultClient,topic, deviceCode, qos);
        if(connectSuccess){
            //连接成功
            LOGGER.info("设备mac:{},连接成功",deviceCode);
        }else{
            LOGGER.info("设备mac:{},连接失败 ",deviceCode);
        }
        if(ignoreError || connectSuccess){
            //发送
            SendMessageRequest sendMessageRequest = new SendMessageRequest();
            sendMessageRequest.setMac(deviceCode);
            sendMessageRequest.setMessage(message);
            sendMessageRequest.setPubTopic(topic);
            sendMessageRequest.setQos(qos);
            SendMessageResponse sendMessageResponse = gatewayDefaultClient.execute(sendMessageRequest);
            boolean sendMessageSuccess = sendMessageResponse.isSuccess();
            if(sendMessageSuccess){
                //发送成功
                LOGGER.info("设备mac:{},发送成功",deviceCode);
            }else{
                LOGGER.info("设备mac:{},发送失败",deviceCode);
            }
        }

        //断开连接
        boolean closeDeviceSuccess = this.close(gatewayDefaultClient, topic, deviceCode, qos);
        if(closeDeviceSuccess){
            //断开成功
            LOGGER.info("设备mac:{},断开成功",deviceCode);
        }else{
            LOGGER.info("设备mac:{},断开失败",deviceCode);
        }

        return true;
    }

    /**
     * 执行自动叫哨
     * @param schedulingDateId
     * @param startTimeQuantum
     * @param endTimeQuantum
     */
    @Override
    public void executeWristlet(Long schedulingDateId,String startTimeQuantum,String endTimeQuantum){
        LOGGER.info("自动叫哨方法执行，参数时间id：{},时间段：{}-{}",schedulingDateId,startTimeQuantum,endTimeQuantum);
        executorService.execute(()->{
            //查询出当前需要叫哨的人员
            Map<String,Object> paramMap = new HashMap<>();
            paramMap.put("schedulingDateId",schedulingDateId);
            paramMap.put("startTimeQuantum",startTimeQuantum);
            paramMap.put("endTimeQuantum",endTimeQuantum);
            List<ShortSchedulingDetail> shortSchedulingDetails = shortSchedulingDetailMapper.selectShortSchedulingDateAndPersonBySchedulingDateIdAndTime(paramMap);
            for (ShortSchedulingDetail shortSchedulingDetail : shortSchedulingDetails) {
                List<ShortPerson> shortPersonList = shortSchedulingDetail.getShortPersonList();
                Long sentryPostId = shortSchedulingDetail.getSentryPostId();
                //根据哨位id查询出哨位名称
                ShortSentryPost shortSentryPost = shortSentryPostMapper.selectShortSentryPostById(sentryPostId);
                String sentryPostName = shortSentryPost.getSentryPostName();
                //查询出腕带设备
                for (ShortPerson shortPerson : shortPersonList) {
                    Long wristbandDeviceId = shortPerson.getWristbandDeviceId();
                    ShortWristbandDervice shortWristbandDervice = shortWristbandDerviceMapper.selectShortWristbandDerviceById(wristbandDeviceId);
                    String derviceCode = shortWristbandDervice.getDerviceCode();
                    Long type = shortWristbandDervice.getType();
                    String message = "请到"+sentryPostName+"上哨";
                    if(type.intValue() == 1){
                        //心率手环，mqtt
                        this.sendMessage(message,derviceCode,0,true);
                    }else if(type.intValue() == 2){
                        //串口
                        SerialPortDefaultClient serialPortDefaultClient = new SerialPortDefaultClient();
                        WristletPortRequest wristletPortRequest = new WristletPortRequest();
                        wristletPortRequest.setDeviceCode(derviceCode);
                        serialPortDefaultClient.execute(wristletPortRequest);
                    }
                    //保存进入消息设备发送集合中
                    autoSendMacList.add(derviceCode.toUpperCase());
                }

            }
        });

    }

    /**
     * 同步时间
     * @param wristDeviceTime
     * @return
     */
    @Override
    public int synchronizationTime(WristDeviceTime wristDeviceTime) {
        Integer deviceType = wristDeviceTime.getDeviceType();
        if(deviceType == null){
            AjaxResult.success("参数错误",HttpStatus.BAD_REQUEST);
        }
        List<String> codeList = new ArrayList<>();

        if(deviceType == 0){
            //全部设备，心率手环
            ShortWristbandDervice shortWristbandDervice = new ShortWristbandDervice();
            shortWristbandDervice.setType(1L);
            List<ShortWristbandDervice> shortWristbandDerviceList = shortWristbandDerviceMapper.selectShortWristbandDerviceList(shortWristbandDervice);
            for (ShortWristbandDervice wristbandDervice : shortWristbandDerviceList) {
                String derviceCode = wristbandDervice.getDerviceCode();
                codeList.add(derviceCode);
            }
        }else if(deviceType == 1){
            //指定设备
            codeList = wristDeviceTime.getCodeList();
        }else{
            AjaxResult.success("参数错误",HttpStatus.BAD_REQUEST);
        }

        if(codeList == null){
            AjaxResult.success("设备列表为空",HttpStatus.BAD_REQUEST);
        }
        List<String> finalCodeList = codeList;
        executorService.execute(()->{
            for (String code : finalCodeList) {
                this.setTime(wristDeviceTime.getTime(),code,0);
            }
        });


        return 1;
    }

    /**
     * 设置参数
     * @param wristDeviceSetting
     * @return
     */
    @Override
    public int setting(WristDeviceSetting wristDeviceSetting) {
        Integer deviceType = wristDeviceSetting.getDeviceType();
        if(deviceType == null || wristDeviceSetting.getSettingMap() == null){
            AjaxResult.success("参数错误",HttpStatus.BAD_REQUEST);
        }
        List<String> codeList = new ArrayList<>();

        if(deviceType == 0){
            //全部设备
            ShortWristbandDervice shortWristbandDervice = new ShortWristbandDervice();
            //设备类型。心率手环
            shortWristbandDervice.setType(1L);
            List<ShortWristbandDervice> shortWristbandDerviceList = shortWristbandDerviceMapper.selectShortWristbandDerviceList(shortWristbandDervice);
            for (ShortWristbandDervice wristbandDervice : shortWristbandDerviceList) {
                String derviceCode = wristbandDervice.getDerviceCode();
                codeList.add(derviceCode);
            }
        }else if(deviceType == 1){
            //指定设备
            codeList = wristDeviceSetting.getCodeList();
        }else{
            AjaxResult.success("参数错误",HttpStatus.BAD_REQUEST);
        }

        if(codeList == null){
            AjaxResult.success("设备列表为空",HttpStatus.BAD_REQUEST);
        }
        List<String> finalCodeList = codeList;
        executorService.execute(()->{
            for (String code : finalCodeList) {
                this.setSetting(wristDeviceSetting.getSettingMap(),code,0);
            }
        });
        return 1;
    }

    /**
     * 连接
     * @param topic
     * @param deviceCode
     * @param qos
     * @return
     */
    private boolean connect(GatewayDefaultClient gatewayDefaultClient,String topic,String deviceCode,int qos){
        //连接
        ConnectDeviceLongRequest connectDeviceLongRequest = new ConnectDeviceLongRequest();
        ConnectDeviceLongData  connectDeviceLongData = new ConnectDeviceLongData();
        connectDeviceLongData.setMac(deviceCode);
        connectDeviceLongRequest.setConnectDeviceLongData(connectDeviceLongData);
        connectDeviceLongRequest.setPubTopic(topic);
        connectDeviceLongRequest.setQos(qos);
        ConnectDeviceLongResponse connectDeviceLongResponse = gatewayDefaultClient.execute(connectDeviceLongRequest);
        boolean connectSuccess = connectDeviceLongResponse.isSuccess();
        return connectSuccess;
    }

    /**
     * 断开连接
     * @param gatewayDefaultClient
     * @param topic
     * @param deviceCode
     * @param qos
     * @return
     */
    private boolean close(GatewayDefaultClient gatewayDefaultClient,String topic,String deviceCode,int qos){
        CloseDeviceRequest closeDeviceRequest = new CloseDeviceRequest();
        closeDeviceRequest.setPubTopic(topic);
        closeDeviceRequest.setQos(qos);
        CloseDeviceData closeDeviceData = new CloseDeviceData();
        closeDeviceData.setMac(deviceCode);
        closeDeviceRequest.setCloseDeviceData(closeDeviceData);
        CloseDeviceResponse closeDeviceResponse = gatewayDefaultClient.execute(closeDeviceRequest);
        boolean closeDeviceSuccess = closeDeviceResponse.isSuccess();
        return closeDeviceSuccess;
    }

    /**
     * 设置时间
     * @param time
     * @param deviceCode
     * @param qos
     * @return
     */
    private boolean setTime(Date time, String deviceCode, int qos){
        //获取网关
        String topicMac = MqttClientUtil.getTopicMac(deviceCode);

        String topic = topicTemplate.replace("#mac#", topicMac);
        GatewayDefaultClient gatewayDefaultClient = new GatewayDefaultClient();
        boolean connectSuccess = this.connect(gatewayDefaultClient,topic, deviceCode, qos);
        if(connectSuccess){
            //连接成功
            LOGGER.info("设备mac:{},连接成功",deviceCode);
        }else{
            LOGGER.info("设备mac:{},连接失败 ",deviceCode);
        }
        //设置时间
        TimingRequest timingRequest = new TimingRequest();
        if(time !=null){
            timingRequest.setDateTime(time);
        }else{
            timingRequest.setDateTime(new Date());
        }
        timingRequest.setMac(deviceCode);
        timingRequest.setPubTopic(topic);
        timingRequest.setQos(qos);
        TimingResponse timingResponse = gatewayDefaultClient.execute(timingRequest);
        boolean setTimingSuccess = timingResponse.isSuccess();
        if(setTimingSuccess){
            LOGGER.info("设备mac:{},设置时间成功",deviceCode);
        }else{
            LOGGER.info("设备mac:{},设置时间失败",deviceCode);
        }
        //断开连接
        boolean closeDeviceSuccess = this.close(gatewayDefaultClient, topic, deviceCode, qos);
        if(closeDeviceSuccess){
            //断开成功
            LOGGER.info("设备mac:{},断开成功",deviceCode);
        }else{
            LOGGER.info("设备mac:{},断开失败",deviceCode);
        }

        return true;
    }

    /**
     * 设置参数
     * @param settingMap
     * @param deviceCode
     * @param qos
     * @return
     */
    private boolean setSetting(Map<String,String> settingMap, String deviceCode, int qos){
        //获取网关
        String topicMac = MqttClientUtil.getTopicMac(deviceCode);

        String topic = topicTemplate.replace("#mac#", topicMac);
        GatewayDefaultClient gatewayDefaultClient = new GatewayDefaultClient();
        boolean connectSuccess = this.connect(gatewayDefaultClient,topic, deviceCode, qos);
        if(connectSuccess){
            //连接成功
            LOGGER.info("设备mac:{},连接成功",deviceCode);
        }else{
            LOGGER.info("设备mac:{},连接失败 ",deviceCode);
        }
        //设置参数
        SettingRequest settingRequest = new SettingRequest();
        settingRequest.setMac(deviceCode);
        settingRequest.setPubTopic(topic);
        settingRequest.setQos(qos);
        settingRequest.setSettingMap(settingMap);
        SettingResponse settingResponse = gatewayDefaultClient.execute(settingRequest);
        if(settingResponse.isSuccess()){
            LOGGER.info("设备mac:{},设置参数成功",deviceCode);
        }else{
            LOGGER.info("设备mac:{},设置参数失败",deviceCode);
        }
        //断开连接
        boolean closeDeviceSuccess = this.close(gatewayDefaultClient, topic, deviceCode, qos);
        if(closeDeviceSuccess){
            //断开成功
            LOGGER.info("设备mac:{},断开成功",deviceCode);
        }else{
            LOGGER.info("设备mac:{},断开失败",deviceCode);
        }
        return closeDeviceSuccess;
    }
}
